package kegg;

import graphs.AMatrixGraph;
import graphs.GraphADT;
import java.util.ArrayList;
import java.util.List;

/**
 * @file Pathway.java
 * @author @author Juan Humanes Ferrer
 * @date 04-Marzo-2014
 */
public class Pathway {

    private String name;
    private String org;
    private String title;
    private List<Entry> entradas;
    private List<Relation> relaciones;
    private GraphADT red;

    /**
     * Clase que representa una ruta metabolica de un organismo. Está compuesta
     * por un conjunto de entradas (lista entradas) que se relacionan entre
     * ellas (lista relaciones).La ruta se representa como un grafo, en el que
     * los vertices son los genes de las entradas y las aristas las relaciones.
     */
    public Pathway() {
        this.entradas = new ArrayList();
        this.relaciones = new ArrayList();
        this.red = new AMatrixGraph();
    }

    /**
     * Método que retorna el identificador de un pathway
     *
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * Método que modifica el identificador de un pathway
     *
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Método que retorna el organismo que posee el pathway
     *
     * @return org
     */
    public String getOrg() {
        return org;
    }

    /**
     * Método que modifica el organismo que posee el pathway
     *
     * @param org
     */
    public void setOrg(String org) {
        this.org = org;
    }

    /**
     * Método que retorna el nombre de un pathway
     *
     * @return title
     */
    public String getTitle() {
        return title;
    }

    /**
     * Método que modifica el nombre de un pathway
     *
     * @param title
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * Método que retorna la lista de entradas de un pathway
     *
     * @return entradas
     */
    public List<Entry> getEntradas() {
        return entradas;
    }

    /**
     * Método que modifica la lista de entradas de un pathway
     *
     * @param entradas
     */
    public void setEntradas(List<Entry> entradas) {
        this.entradas = entradas;
    }

    /**
     * Método que añade una entrada al pathway
     *
     * @param entrada
     */
    public void addEntrada(Entry entrada) {
        entradas.add(entrada);

    }

    /**
     * Método que retorna la lista de relaciones de un pathway
     *
     * @return relaciones
     */
    public List<Relation> getRelaciones() {
        return relaciones;
    }

    /**
     * Método que modifica la lista de relaciones de un pathway
     *
     * @param relaciones
     */
    public void setRelaciones(List<Relation> relaciones) {
        this.relaciones = relaciones;
    }

    /**
     * Método que añade una relacion al pathway
     *
     * @param relacion
     */
    public void addRelacion(Relation relacion) {
        this.relaciones.add(relacion);
    }

    /**
     * Método que retorna la red de genes de un pathway
     *
     * @return red
     */
    public GraphADT getRed() {
        return red;
    }

    /**
     * Método que modifica la red de genes de un pathway
     *
     * @param red
     */
    public void setRed(GraphADT red) {
        this.red = red;
    }

    /**
     * Método que devuelve la lista de genes de un pathway. Añade a la lista el
     * gen o conjunto de genes de cada entrada.
     *
     * @return genes
     */
    public List<String> listaGenes() {

        List<String> genes = new ArrayList();
        for (int i = 0; i < this.getEntradas().size(); i++) {
            /*Solo las entradas de tipo "gene" tienen genes. Las de tipo "group"
             estan formadas por entradas*/
            if (this.getEntradas().get(i).getType().equals("gene")) {
                String entrada = this.getEntradas().get(i).getName();
                //Se crea una lista g con cada gen con formato "org:gen"
                String[] g = entrada.split(" ");
                for (String g1 : g) {
                    //Por cada gen, se separa para extraer el nombre
                    String[] g2 = g1.split(":");
                    int a = g2.length;
                    if (a > 1) {
                        //Si el gene no se encuentra ya en la lista, se añade
                        if (!genes.contains(g2[1])) {

                            genes.add(g2[1]);
                        }
                    }
                }
            }

        }

        return genes;
    }

    /**
     * Método que devuelve la lista de genes de una entrada de un pathway. Si la
     * entrada es de tipo "gen" añade a la lista el gen o conjunto de genes. Si
     * es de tipo "group", añade los genes de cada entrada que forman parte de
     * dicha entrada.
     *
     * @param entrada
     * @return
     */
    public List<String> listaGenesEntrada(Entry entrada) {
        //LOGGER.info("\n\nSe accede al metodo\n");
        List<String> genes = new ArrayList();
        String nombre;
        String[] g;
        switch (entrada.getType()) {
            case "gene":
                //Se extraen los genes de la entrada
                nombre = entrada.getName();
                g = nombre.split(" ");
                for (String g1 : g) {
                    String[] g2 = g1.split(":");
                    int a = g2.length;
                    if (a > 1) {
                        if (!genes.contains(g2[1])) {

                            genes.add(g2[1]);
                        }
                    }
                }
                break;
            case "group":
                int i;
                /*Se extraen los genes de cada entrada que forma la lista de 
                 componentes de una entrada*/
                for (i = 0; i < entrada.getComponents().size(); i++) {
                    String id = entrada.getComponents().get(i).toString();
                    int posicion = entradaPorID(id) - 1;
                    nombre = this.entradas.get(posicion).getName();
                    g = nombre.split(" ");
                    for (String g1 : g) {
                        String[] g2 = g1.split(":");
                        int a = g2.length;
                        if (a > 1) {
                            if (!genes.contains(g2[1])) {

                                genes.add(g2[1]);
                            }
                        }
                    }
                }
                break;
        }

        //LOGGER.info("\n\nSe sale del metodo\n");
        return genes;
    }

    /**
     * Método que recibe el identificador de una entrada y devuelve su posición
     * en la lista de entradas del pathway
     *
     * @param id
     * @return i
     */
    public int entradaPorID(String id) {
        //LOGGER.info("\n\nSe accede al metodo\n");
        int i = 0;
        boolean enc = false;
        while (!enc) {
            if (this.entradas.get(i).getId().equals(id)) {
                enc = true;
            }
            i++;
        }
        //LOGGER.info("\n\nSe sale del metodo\n");
        return i;
    }

    /**
     * Crea el grafo de genes de un pathway
     *
     * @return grafo
     */
    public GraphADT red_Genes() {

        // LOGGER.info("\n\nSe accede del metodo\n");
        GraphADT grafo = new AMatrixGraph();

        for (int i = 0; i < this.getEntradas().size(); i++) {
            if (this.getEntradas().get(i).getType().equals("group")) {
                List<String> lista = listaGenesEntrada(this.getEntradas().get(i));
                int posicion = grafo.size();
                List posiciones = new ArrayList();
                for (String lista1 : lista) {
                    //Se comprueba si el gen ya se ha añadido como vértice al grafo
                    int vertice = grafo.existeVertice(lista1);
                    if (vertice == -1) {
                        grafo.addVertex(lista1);
                        //Se le asigna una poscion al vértice
                        posiciones.add(posicion);
                        posicion++;
                    } else {
                        posiciones.add(vertice);
                    }
                }
                for (int r = 0; r < posiciones.size(); r++) {
                    for (int s = r + 1; s < posiciones.size(); s++) {
                        grafo.add2Edge((Integer) posiciones.get(r), (Integer) posiciones.get(s));

                    }

                }
            }
        }


        /* Se hace un recorrido de las relaciones de un pathway
         Si la relacion es de subtipo "compound", se relacionan todos con 
         todos, tanto entre genes de la misma entrada, como con la otra 
         entrada con la que se relacionan
         */
        for (int i = 0; i < this.getRelaciones().size(); i++) {
            if (getRelaciones().get(i).getType().equals("compound")) {
                List<String> lista1 = listaGenesEntrada(getRelaciones().get(i).getEntrada1());
                List<String> lista2 = listaGenesEntrada(getRelaciones().get(i).getEntrada2());

                int posicion = grafo.size();
                List posiciones = new ArrayList();
                for (String lista11 : lista1) {
                    //Se comprueba si el gen ya se ha añadido como vértice al grafo
                    int vertice = grafo.existeVertice(lista11);
                    if (vertice == -1) {
                        grafo.addVertex(lista11);
                        //Se le asigna una poscion al vértice
                        posiciones.add(posicion);
                        posicion++;
                    } else {
                        posiciones.add(vertice);
                    }
                }
                for (String lista21 : lista2) {
                    int vertice2 = grafo.existeVertice(lista21);
                    if (vertice2 == -1) {
                        grafo.addVertex(lista21);
                        posiciones.add(posicion);
                        posicion++;
                    } else {
                        posiciones.add(vertice2);
                    }
                }

                //Se añade una arista por cada combinación de genes 
                for (int r = 0; r < posiciones.size(); r++) {
                    for (int s = r + 1; s < posiciones.size(); s++) {
                        grafo.add2Edge((Integer) posiciones.get(r), (Integer) posiciones.get(s));

                    }

                }

            } /* Si la relacion no es de tipo "compound", un gen de una entrada se relaciona
             con todos los genes de la otra entrada, pero no con los de su misma 
             entrada.
             */ else {

                List<String> lista1 = listaGenesEntrada(getRelaciones().get(i).getEntrada1());
                List<String> lista2 = listaGenesEntrada(getRelaciones().get(i).getEntrada2());

                int posicion = grafo.size();
                List posiciones1 = new ArrayList();
                List posiciones2 = new ArrayList();
                for (String lista11 : lista1) {
                    int vertice = grafo.existeVertice(lista11);
                    if (vertice == -1) {
                        grafo.addVertex(lista11);
                        posiciones1.add(posicion);
                        posicion++;
                    } else {
                        posiciones1.add(vertice);
                    }
                }
                for (String lista21 : lista2) {
                    int vertice2 = grafo.existeVertice(lista21);
                    if (vertice2 == -1) {
                        grafo.addVertex(lista21);
                        posiciones2.add(posicion);
                        posicion++;
                    } else {
                        posiciones2.add(vertice2);
                    }
                }

                for (Object posiciones11 : posiciones1) {
                    for (Object posiciones21 : posiciones2) {
                        grafo.add2Edge((Integer) posiciones11, (Integer) posiciones21);
                    }
                }

            }

        }
        //Se le asigna el grafo creado al pathway
        setRed(grafo);

        //LOGGER.info("\n\nSe sale del metodo\n");
        return grafo;
    }
}
